package com.nanachi.feign.core;

import com.nanachi.feign.core.annotation.EnableSimpleFeignClient;
import com.nanachi.feign.core.annotation.SimpleFeignClient;
import com.nanachi.feign.core.context.SimpleFeignSpecification;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.ClassUtils;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author yuhongtai
 * feignClient注册器
 */
@Slf4j
public final class EnableSimpleFeignClientRegistrar
        implements ImportBeanDefinitionRegistrar, EnvironmentAware, ResourceLoaderAware {

    private Environment environment;

    private ResourceLoader resourceLoader;

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        Map<String, Object> annotationAttributes = getAnnotationAttributes(metadata, EnableSimpleFeignClient.class);
        if (annotationAttributes == null) {
            log.warn(String.format("class %s 's annotationAttributesMap is null, metadata: %s",
                    EnableSimpleFeignClient.class.getName(), metadata.getClass().getName()));
            return;
        }
        registerDefaultConfiguration(metadata, registry);
        // 注册feignClient
        registerFeignClients(metadata, registry);
    }

    private Map<String, Object> getAnnotationAttributes(AnnotationMetadata metadata, Class<?> clazz) {
        Map<String, Object> annotationAttributes = metadata.
                getAnnotationAttributes(clazz.getName());

        return annotationAttributes;
    }

    /**
     * 注册默认配置。默认配置均以"default." 开头
     */
    private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        Map<String, Object> annotationAttributes = getAnnotationAttributes(metadata, EnableSimpleFeignClient.class);
        if (annotationAttributes == null) {
            log.warn(String.format("class %s 's annotationAttributesMap is null, metadata: %s",
                    EnableSimpleFeignClient.class.getName(), metadata.getClass().getName()));
            return;
        }
        Class<?>[] defaultConfigurations = (Class<?>[]) annotationAttributes.get("defaultConfigurations");
        if (defaultConfigurations != null && defaultConfigurations.length != 0) {
            String name;
            // 拼接名称要用顶部类
            if (metadata.hasEnclosingClass()) {
                name = "default." + metadata.getEnclosingClassName();
            } else {
                name = "default." + metadata.getClassName();
            }
            registerConfiguration(registry, name, defaultConfigurations);
        }
    }

    private void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        Map<String, Object> annotationAttributes = getAnnotationAttributes(metadata, EnableSimpleFeignClient.class);
        if (annotationAttributes == null) {
            log.warn(String.format("class %s 's annotationAttributesMap is null, metadata: %s",
                    EnableSimpleFeignClient.class.getName(), metadata.getClass().getName()));
            return;
        }
        List<BeanDefinition> candidateBeanDefinitionList = new LinkedList<>();
        String[] packages = (String[]) annotationAttributes.get("packages");
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        scanner.setEnvironment(environment);
        scanner.setResourceLoader(resourceLoader);
        scanner.addIncludeFilter(new AnnotationTypeFilter(SimpleFeignClient.class));
        if (packages != null && packages.length > 0) {
            for (String path : packages) {
                Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(path);
                candidateBeanDefinitionList.addAll(candidateComponents);
            }
        } else {
            Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents("com.nanachi.feign.test.*");
            candidateBeanDefinitionList.addAll(candidateComponents);
        }

        for (BeanDefinition beanDefinition : candidateBeanDefinitionList) {
            if (beanDefinition instanceof AnnotatedBeanDefinition) {
                AnnotatedBeanDefinition candidateBeanDefinition = (AnnotatedBeanDefinition) beanDefinition;
                AnnotationMetadata feignMetadata = candidateBeanDefinition.getMetadata();
                Map<String, Object> feignAnnotationAttributes = feignMetadata.getAnnotationAttributes(SimpleFeignClient.class.getName());
                String feignName = getFeignName(feignAnnotationAttributes);
                // 注册feign配置类,但还未放到Context中
                registerConfiguration(registry, feignName, feignAnnotationAttributes.get("config"));
                // 注册feign实例
                registerFeignClient(registry, feignAnnotationAttributes, feignMetadata);
            }
        }
    }

    private ClassPathScanningCandidateComponentProvider getScanner() {
        ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false, this.environment) {
            @Override
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                AnnotationMetadata metadata = beanDefinition.getMetadata();
                // 是独立存在的类、接口、注解（指能通过名称直接放问）
                if (metadata.isIndependent()) {
                    // 不是注解
                    return !metadata.isAnnotation();
                }
                return false;
            }
        };
        return provider;
    }

    private String getFeignName(@NotNull Map<String, Object> feignAnnotatedMetadata) {
        String name;
        name = (String) feignAnnotatedMetadata.get("value");
        if (StringUtils.isBlank(name)) {
            name = (String) feignAnnotatedMetadata.get("name");
        }
        return name;
    }

    public void registerFeignClient(BeanDefinitionRegistry registry
            , Map<String, Object> attributes, AnnotationMetadata metadata) {
        String className = metadata.getClassName();
        Class clazz = ClassUtils.resolveClassName(className, null);
        ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory
                ? (ConfigurableBeanFactory) registry : null;
        // 代理feign的工厂类，实现初始化的逻辑
        SimpleFeignClientFactoryBean factoryBean = new SimpleFeignClientFactoryBean();
        String feignName = getFeignName(attributes);
        factoryBean.setBeanFactory(beanFactory);
        factoryBean.setName(feignName);
        factoryBean.setType(clazz);
        factoryBean.setPath((String) attributes.get("path"));
        BeanDefinitionBuilder builder = BeanDefinitionBuilder
                .genericBeanDefinition(clazz, factoryBean::getObject);
        builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
        builder.setLazyInit(true);
        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
        // TODO 注册factoryBean返回的beanDefinition
        beanDefinition.setPrimary(true);
        BeanDefinitionReaderUtils.registerBeanDefinition(new BeanDefinitionHolder(beanDefinition, feignName), registry);
    }

    //

    /**
     * 注册beanDefinition
     *
     * @param name          beanDefinition名称
     * @param configuration 配置类数组
     * @param registry      注册器，默认为DefaultListableBeanFactory
     */
    private void registerConfiguration(BeanDefinitionRegistry registry, String name, Object configuration) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder
                .genericBeanDefinition(SimpleFeignSpecification.class);
        // 添加构造参数
        builder.addConstructorArgValue(name)
                .addConstructorArgValue(configuration);
        registry.registerBeanDefinition(name + SimpleFeignSpecification.class.getName(),
                builder.getBeanDefinition());
    }


}
